package com.stu.info;


import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import android.graphics.PointF;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.stu.aq.*;
import com.stu.graph.*;
import com.stu.usb.SerialIOManager;
import com.stu.widget.*;

public class SensorGraphFragment extends GraphFragment implements SerialIOManager.Listener {
	private int xValue;
	private Timer timer;
	private boolean isAlive;
	private boolean stopFlag;
	private StatusManager statusManager;
	private String statusStr;

	private class Status {
		float	gyroR, gyroP, gyroY, accelR, accelP, accelZ, magX, magY, magZ;
	}
	
	public SensorGraphFragment() {
		xValue = 0;
		initData();
		statusManager = new StatusManager();
	}

	private void initData() {
		graphSeries = new ArrayList<DataSeries>(AQ.Sensor.COUNT);
		graphSeries.add( AQ.Sensor.GYRO_ROLL, new DataSeries("Gyro Roll", new DataStyle(AQ.Colors[0], 3)) );
		graphSeries.add( AQ.Sensor.GYRO_PITCH, new DataSeries("Gyro Pitch", new DataStyle(AQ.Colors[1], 3)) );
		graphSeries.add( AQ.Sensor.GYRO_YAW, new DataSeries("Gyro Yaw", new DataStyle(AQ.Colors[2], 3)) );
		graphSeries.add( AQ.Sensor.ACCEL_ROLL, new DataSeries("Accel Roll", new DataStyle(AQ.Colors[3], 3)) );
		graphSeries.add( AQ.Sensor.ACCEL_PITCH, new DataSeries("Accel Pitch", new DataStyle(AQ.Colors[4], 3)) );
		graphSeries.add( AQ.Sensor.ACCEL_Z, new DataSeries("Accel Z", new DataStyle(AQ.Colors[5], 3)) );
		graphSeries.add( AQ.Sensor.MAG_X, new DataSeries("Mag X", new DataStyle(AQ.Colors[6], 3)) );
		graphSeries.add( AQ.Sensor.MAG_Y, new DataSeries("Mag Y", new DataStyle(AQ.Colors[7], 3)) );
		graphSeries.add( AQ.Sensor.MAG_Z, new DataSeries("Mag Z", new DataStyle(AQ.Colors[8], 3)) );
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
	  Bundle savedInstanceState) {
		View v = inflater.inflate( R.layout.graph, container, false );
		init( v, "Sensor", 50, GraphView.GraphViewConfig.GRID_Y, 50, 50 );
		updateViews();
		return v;
	}

	@Override
	public void onDestroyView() {
		super.onDestroyView();
		if( timer != null )
			timer.cancel();
	}

	/*
	 * 0 Gyro Roll
	 * 1 Gyro Pitch
	 * 2 Gyro Yaw
	 * 3 Accel Roll
	 * 4 Accel Pitch
	 * 5 Accel Z
	 * 6 Mag X
	 * 7 Mag Y
	 * 8 Mag Z
	 */
	@Override
	public void onNewData(byte[] data) {
if(AQ.DEBUG) Log.d("sensor", "onNewData");
		isAlive = true;
		if( statusStr == null )
			statusStr = new String( data );
		else
			statusStr += new String( data );
		if( stopFlag ) {
if(AQ.DEBUG) Log.d("sensor", "stop");
			statusStr = null;
			return;
		}
		// make sure we have the entire status by
		//  looking for a newline at the end
		int ix = statusStr.length();
		if( statusStr.charAt(ix-1) != '\n' )
			return;
		statusStr = statusStr.trim();	// remove newline and trailing spaces
		ix = statusStr.length();
		// don't include trailing comma in substring
		if( statusStr.charAt(ix-1) == ',' )
			ix--;
		final String values[] = statusStr.substring(0, ix).split( "," );
		if( values.length != AQ.Sensor.COUNT ) {
			statusStr = null;
			mListener.OnCommandWarning( getId(), new AQException(SensorGraphFragment.class.getSimpleName() +
			  ": Expected length " + String.valueOf(AQ.Sensor.COUNT) + " got " + String.valueOf(values.length)) );
			return;
		}
		Status s = new Status();
		try {
			s.gyroR = Float.parseFloat( values[0] );
			s.gyroP = Float.parseFloat( values[1] );
			s.gyroY = Float.parseFloat( values[2] );
			s.accelR = Float.parseFloat( values[3] );
			s.accelP = Float.parseFloat( values[4] );
			s.accelZ = Float.parseFloat( values[5] );
			s.magX = Float.parseFloat( values[6] );
			s.magY = Float.parseFloat( values[7] );
			s.magZ = Float.parseFloat( values[8] );
		} catch( NumberFormatException e ) {
			mListener.OnCommandError( getId(), new AQException(SensorGraphFragment.class.getSimpleName() +
			  ": NumberFormatException: " + e.getMessage()) );
			statusStr = null;
			return;
			
		}
		statusStr = null;
		statusManager.handleState( new StatusData(this, s) );
	}

	@Override
	public void onRunError(Exception e) {
	}
	
	@Override
	public void stopUpdate( OnCommandListener l, SerialIOManager io ) {
if(AQ.DEBUG) Log.d("sensor", "stopUpdate");
		mListener = l;
		stopFlag = true;
		timer.cancel();
		io.setListener( this );
	    String s = "x";
	    io.writeAsync( s.getBytes() );
	    // onNewData and updateUI may not be called, depending on
	    //  what still remains in the IO buffer so call the listener here
    	mListener.OnCommandComplete( getId(), FragmentState.STOPPING );
	}

	@Override
	public void update(OnCommandListener l, SerialIOManager io) {
if(AQ.DEBUG) Log.d("sensor", "update");
		xValue = 0;
		if( graphSeries == null )
			initData();		// in case clear() was called
		mListener = l;
		stopFlag = false;
		isAlive = false;
		io.setListener( this );
	    String s = "i";
	    io.writeAsync( s.getBytes() );
		// Make sure we are getting status
		timer = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				checkIfAlive();
				isAlive = false;
			}
		};
		timer.scheduleAtFixedRate( tt, 1000, 1000 );
	}

	private void checkIfAlive() {
		if( !isAlive ) {
			timer.cancel();
			if( mListener != null )
				mListener.OnCommandError( getId(), new AQException("No transmitter data from AeroQuad") );
		}
	}

	@Override
	public void updateUI(Object statusData) {
		if( statusData != null && graphSeries != null ) {
			Status s = (Status)statusData;
			graphSeries.get(AQ.Sensor.GYRO_ROLL).appendData( new PointF(xValue, s.gyroR) );
			graphSeries.get(AQ.Sensor.GYRO_PITCH).appendData( new PointF(xValue, s.gyroP) );
			graphSeries.get(AQ.Sensor.GYRO_YAW).appendData( new PointF(xValue, s.gyroY) );
			graphSeries.get(AQ.Sensor.ACCEL_ROLL).appendData( new PointF(xValue, s.accelR) );
			graphSeries.get(AQ.Sensor.ACCEL_PITCH).appendData( new PointF(xValue, s.accelP) );
			graphSeries.get(AQ.Sensor.ACCEL_Z).appendData( new PointF(xValue, s.accelZ) );
			graphSeries.get(AQ.Sensor.MAG_X).appendData( new PointF(xValue, s.magX) );
			graphSeries.get(AQ.Sensor.MAG_Y).appendData( new PointF(xValue, s.magY) );
			graphSeries.get(AQ.Sensor.MAG_Z).appendData( new PointF(xValue, s.magZ) );
			updateViews();
			xValue++;
		}
		mListener.OnCommandComplete( getId(), FragmentState.UPDATING );
	}

	@Override
	public void clear() {
		super.clear();
		graphSeries = null;
	}
}
